package com.ima.common.service.support;

import com.ima.common.repository.support.IGenericRepository;
import com.ima.common.repository.utils.DynamicSpecifications;
import com.ima.common.repository.utils.SearchFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;

public abstract class GenericService<E, PK extends Serializable>
        implements IGenericService<E, PK> {

    protected Logger log = LoggerFactory.getLogger(getClass());

    @PersistenceContext
    protected EntityManager em;

    protected final Class<E> entityClass;

    public GenericService(Class<E> entityClass) {
        this.entityClass = entityClass;
    }

    protected abstract IGenericRepository<E, PK> getRepository();

    @Override
    @Transactional(rollbackFor = Exception.class)
    public <S extends E> S save(S entity) throws Exception {
        return getRepository().save(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int save(Iterable<? extends E> entities) throws Exception {
        return getRepository().save(entities).size();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public <S extends E> S update(S entity) throws Exception {
        return getRepository().update(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int update(Iterable<? extends E> entities) throws Exception {
        int i = 0;
        if (entities == null) {
            return i;
        }

        for (E e : entities) {
            getRepository().update(e);
            i++;
        }
        return i;
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public E find(PK id) {
        return getRepository().findOne(id);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public E find(Specification<E> spec) {
        return getRepository().findOne(spec);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public boolean exists(PK id) {
        return getRepository().exists(id);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public List<E> findAll() {
        return getRepository().findAll();
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public List<E> findAll(Sort sort) {
        return getRepository().findAll(sort);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public List<E> findAll(Iterable<PK> ids) {
        return getRepository().findAll(ids);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public List<E> findAll(Specification<E> spec) {
        return getRepository().findAll(spec);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public List<E> findAll(Collection<SearchFilter> filters) {
        Specification<E> spec = DynamicSpecifications.bySearchFilter(filters);
        return getRepository().findAll(spec);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public Page<E> findAll(Specification<E> spec, Pageable pageable) {
        return getRepository().findAll(spec, pageable);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public Page<E> findAll(Collection<SearchFilter> filters, Pageable pageable) {
        Specification<E> spec = DynamicSpecifications.bySearchFilter(filters);
        return getRepository().findAll(spec, pageable);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public Page<E> findAll(Specification<E> spec, Sort sort, Pageable pageable) {
        return getRepository().findAll(spec, pageable);
    }


    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public List<E> findAll(Specification<E> spec, Sort sort) {
        return getRepository().findAll(spec, sort);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public long count() {
        return getRepository().count();
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public long count(Specification<E> spec) {
        return getRepository().count(spec);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(PK id) throws Exception {
        getRepository().delete(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(E entity) throws Exception {
        getRepository().delete(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Iterable<? extends E> entities) throws Exception {
        getRepository().delete(entities);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAll() throws Exception {
        getRepository().deleteAll();
    }

    public void setEm(EntityManager em) {
        this.em = em;
    }


}
